package com.xuecheng.search.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.xuecheng.commons.model.dto.CoursePubIndexDto;
import com.xuecheng.commons.model.dto.PageRequest;
import com.xuecheng.commons.model.vo.CoursePubIndexVo;
import com.xuecheng.commons.model.vo.CoursePubVo;
import com.xuecheng.commons.model.vo.PageResponseResult;
import com.xuecheng.commons.model.vo.ResponseResult;
import com.xuecheng.commons.utils.BeanHelper;
import com.xuecheng.content.api.ContentClient;
import com.xuecheng.search.domain.CoursePubIndex;
import com.xuecheng.search.service.SearchService;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private ContentClient contentClient;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 同步索引库
     *
     * @param coursePubId
     */
    @Override
    public void saveDocument(Long coursePubId) throws IOException {
        ResponseResult<CoursePubVo> responseResult = contentClient.findById(coursePubId);
        if (responseResult.getCode() == 0) {
            CoursePubVo coursePubVo = responseResult.getData();
            CoursePubIndex coursePubIndex = BeanHelper.copyProperties(coursePubVo, CoursePubIndex.class);
            coursePubIndex.setPrice(coursePubVo.getPrice().floatValue());//TODO 传过来价格有问题
            coursePubIndex.setCourse_id(coursePubVo.getCourseId());
            coursePubIndex.setCompany_id(coursePubVo.getCompanyId());
            coursePubIndex.setIs_pub(coursePubVo.getIsPub());
            coursePubIndex.setIs_latest(coursePubVo.getIsLatest());
            coursePubIndex.setCreate_date(new Date());
            coursePubIndex.setId(coursePubVo.getCourseId());

            IndexRequest request = new IndexRequest("course").id(coursePubId.toString());
            request.source(JSON.toJSONString(coursePubIndex), XContentType.JSON);
            restHighLevelClient.index(request, RequestOptions.DEFAULT);
        }
    }

    /**
     * 根据id查询课程发布
     *
     * @param pageRequest
     * @param coursePubIndexDto
     * @return
     */
    @Override
    public ResponseResult searchCourse(PageRequest pageRequest, CoursePubIndexDto coursePubIndexDto) {
        try {
            SearchRequest request = new SearchRequest("course");
            buildBasicQuery(coursePubIndexDto, request, pageRequest);

            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

            return handleResponse(response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 课程下架后删除对应的索引
     *
     * @param courseUnpunId
     */
    @Override
    public void deleteDocument(Long courseUnpunId) throws IOException {
        //准备Request
        DeleteRequest request = new DeleteRequest("course", String.valueOf(courseUnpunId));
        //发送请求
        restHighLevelClient.delete(request, RequestOptions.DEFAULT);
    }

    private void buildBasicQuery(CoursePubIndexDto coursePubIndexDto, SearchRequest request, PageRequest pageRequest) {
        // 1.构建BooleanQuery
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //关键字搜索
        String key = coursePubIndexDto.getKeyword();
        if (StrUtil.isBlank(key)) {
            boolQuery.must(QueryBuilders.matchAllQuery());
        } else {
            boolQuery.must(QueryBuilders.matchQuery("all", key));
        }
        //其他条件过滤
        if (StrUtil.isNotBlank(coursePubIndexDto.getMt())) {
            boolQuery.filter(QueryBuilders.termQuery("mt", coursePubIndexDto.getMt()));
        }
        if (StrUtil.isNotBlank(coursePubIndexDto.getSt())) {
            boolQuery.filter(QueryBuilders.termQuery("st", coursePubIndexDto.getSt()));
        }
        if (StrUtil.isNotBlank(coursePubIndexDto.getGrade())) {
            boolQuery.filter(QueryBuilders.termQuery("grade", coursePubIndexDto.getGrade()));
        }

        /*//算分控制
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
                //原始查询
                boolQuery,
                //function score的数组
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                QueryBuilders.termQuery("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction(10)
                        )
                }
        );*/
        request.source().query(boolQuery);
        //分页
        int page = pageRequest.getPageNo();
        int size = pageRequest.getPageSize();
        request.source().from((page - 1) * size).size(size);
        //高亮
        request.source().highlighter(new HighlightBuilder()
                .field("name")
                .preTags("<font color=red>")
                .postTags("</font>")
                .requireFieldMatch(false));
    }

    private ResponseResult handleResponse(SearchResponse response) {
        //解析响应
        SearchHits searchHits = response.getHits();
        //获取总条数
        long total = searchHits.getTotalHits().value;
        //文档数组
        SearchHit[] hits = searchHits.getHits();
        List<CoursePubIndexVo> voList = new ArrayList<>();
        //遍历
        for (SearchHit hit : hits) {
            //获取文档source
            String json = hit.getSourceAsString();
            //反序列化
            CoursePubIndexVo coursePubIndexVo = JSON.parseObject(json, CoursePubIndexVo.class);
            coursePubIndexVo.setIndexId(coursePubIndexVo.getCourse_id()); // 设置索引id
            /*//获得排序值
            Object[] sortValues = hit.getSortValues();
            if (sortValues.length > 0) {
                Object sortValue = sortValues[0];
                coursePubIndexVo.setDistance(sortValue);
            }*/
            Map<String, HighlightField> highlightFieldMap = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFieldMap)) {
                //根据字段名获取高亮结果
                HighlightField highlightField = highlightFieldMap.get("name");
                if (highlightField != null) {
                    //获取高亮值
                    String name = highlightField.getFragments()[0].string();
                    coursePubIndexVo.setName(name);
                }
            }
            voList.add(coursePubIndexVo);
        }
        return PageResponseResult.okResult(total, voList);
    }

    /**
     * 根据id查询课程
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult findById(String id) throws IOException {
        GetRequest request = new GetRequest("course", id);
        GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
        String json = response.getSourceAsString();
        CoursePubIndexVo coursePubIndexVo = JSON.parseObject(json, CoursePubIndexVo.class);
        coursePubIndexVo.setIndexId(coursePubIndexVo.getCourse_id());
        return ResponseResult.okResult(coursePubIndexVo);
    }
}
